#include "TournamentSelectionGA.h"

#include <cstring>
using std::memcpy;

#include "../../base.h"
#include "../../math/Random.h"
#include "../../qt/Logger.h"

#include "Individual.h"
#include "ThreadedPopulationEvaluator.h"

namespace GA {

/*
	TournamentSelectionGA
*/

/*	public	*/

TournamentSelectionGA::TournamentSelectionGA(int nPopulation, Individual* const* initialPopulation) :
	_nPopulation(nPopulation),
	_useElitism(false),
	_bestIndividualCurrent(0),
	_bestIndividualEver(0) {

	/* population */
	_population = new Individual*[nPopulation];
	memcpy(_population, initialPopulation, _nPopulation * sizeof(Individual*));

	_evaluatePopulation();
}

TournamentSelectionGA::~TournamentSelectionGA() {
	/* population */
	deleteall<Individual>(_population, _nPopulation);
	delete[] _population;

	/* bests */
	delete _bestIndividualCurrent;
	delete _bestIndividualEver;
}

void TournamentSelectionGA::evolveOneStep() {
//	Logger::debug(">>> TournamentSelectionGA::evolveOneStep()");

	/* new population */
	Individual** newPopulation = new Individual*[_nPopulation];

	/* index */
	int index = 0;

	/* elitism */
	if (_useElitism) {
		newPopulation[index] = _population[0]->copy();
		++index;
	}

	/* fill new population */
	for(; index != _nPopulation; ++index) {

		/* fit parents */
		Individual* parent1 = _pickParent();
		Individual* parent2 = _pickParent();

		/* mate & store */
		newPopulation[index] = parent1->mate(*parent2);
	}

	/* delete old population */
	deleteall<Individual>(_population, _nPopulation);
	delete[] _population;

	/* store new population */
	_population = newPopulation;

	_evaluatePopulation();

	/* best current */
	delete _bestIndividualCurrent;
	_bestIndividualCurrent = _population[0]->copy();

	/* best ever */
	if (_bestIndividualEver == 0 || *_bestIndividualCurrent > *_bestIndividualEver) {
		delete _bestIndividualEver;
		_bestIndividualEver = _bestIndividualCurrent->copy();
	}
}

void TournamentSelectionGA::setUseElitism(bool flag) {
	/* elitism flag */
	_useElitism = flag;
}

const Individual& TournamentSelectionGA::getBestIndividualCurrent() const {
	assert(_bestIndividualCurrent != 0);
	return *_bestIndividualCurrent;
}

const Individual& TournamentSelectionGA::getBestIndividualEver() const {
	assert(_bestIndividualEver != 0);
	return *_bestIndividualEver;
}

/* private */

void TournamentSelectionGA::_placeBestIndividualFirst() {
	/* place best individual in first position */
	for(int i = 1; i != _nPopulation; ++i) {

		if (*(_population[i]) > *(_population[0])) {
			/* swap individuals */
			Individual* tmp = _population[0];
			_population[0] = _population[i];
			_population[i] = tmp;
		}
	}
}

void TournamentSelectionGA::_evaluatePopulation() {

	/* evaluate */
	ThreadedPopulationEvaluator populationEvaluator(_nPopulation, _population);
	populationEvaluator.evaluate();

	/* best first */
	_placeBestIndividualFirst();
}

Individual* TournamentSelectionGA::_pickParent() {

	/* random */
	Random random = Random::getInstance();

	/* random parents */
	Individual* parent1 = _population[random.getInt(_nPopulation)];
	Individual* parent2 = _population[random.getInt(_nPopulation)];

	/* choose fit parent */
	if (*parent1 > *parent2) {
		return parent1;
	} else {
		return parent2;
	}
}

} // GA

